


#include <getopt.h>
#include "qelib.h"
#include "lz4.h"
#include "lz4file.h"


static qe_ret lz4_file_decompare(const char *input, const char *output)
{
    qe_ret ret = qe_ok;
    LZ4_readFile_t *reader;
    LZ4F_errorCode_t lz4_ret = LZ4F_OK_NoError;

    FILE *inpfp = fopen(input, "rb");
    if (!inpfp) {
        qe_error("open %s error", input);
        ret = qe_err_io;
        goto __exit;
    }

    FILE *outfp = fopen(output, "wb");
    if (!inpfp) {
        qe_error("open %s error", output);
        ret = qe_err_io;
        goto __exit;
    }

    lz4_ret = LZ4F_readOpen(&reader, inpfp);
    if (LZ4F_isError(lz4_ret)) {
        qe_error("lz4f open error");
        ret = qe_err_common;
        goto __exit;
    }

    qe_gbuf *buf = qe_gbuf_new(1024*1024);
    if (!buf) {
        qe_error("create buffer error");
        ret = qe_err_mem;
        goto __exit;
    }

    while (1) {
        lz4_ret = LZ4F_read(reader, buf->p, buf->nbytes);
        if (LZ4F_isError(lz4_ret)) {
            qe_error("LZ4F_read error: %s", LZ4F_getErrorName(lz4_ret));
            goto __exit;
        }
        /* nothing to read */
        if (lz4_ret == 0) {
            break;
        }
        if (fwrite(buf->p, 1, lz4_ret, outfp) != lz4_ret) {
            qe_error("write error!");
            goto __exit;
        }
    }

__exit:
    if (reader) {
        LZ4F_readClose(reader);
    }
    if (inpfp)
        fclose(inpfp);
    if (outfp)
        fclose(outfp);
    if (buf)
        qe_free(buf);
    return ret;
}

static qe_ret lz4_file_compare(const char *input, const char *output)
{
    qe_ret ret = qe_ok;
    LZ4_writeFile_t* writer;
    LZ4F_errorCode_t lz4_ret = LZ4F_OK_NoError;
    
    FILE *inpfp = fopen(input, "rb");
    if (!inpfp) {
        qe_error("open %s error", input);
        ret = qe_err_io;
        goto __exit;
    }

    FILE *outfp = fopen(output, "wb");
    if (!inpfp) {
        qe_error("open %s error", output);
        ret = qe_err_io;
        goto __exit;
    }

    qe_gbuf *buf = qe_gbuf_new(1024*8);
    if (!buf) {
        qe_error("create buffer error");
        ret = qe_err_mem;
        goto __exit;
    }

    lz4_ret = LZ4F_writeOpen(&writer, outfp, QE_NULL);
    if (LZ4F_isError(lz4_ret)) {
        qe_error("lz4f open error");
        ret = qe_err_common;
        goto __exit;
    }

    while (1) {
        int len = fread(buf->p, 1, buf->nbytes, inpfp);
        if (len == 0) {
            break;
        }
        lz4_ret = LZ4F_write(writer, buf, len);
        if (LZ4F_isError(lz4_ret)) {
            qe_error("LZ4F_write: %s", LZ4F_getErrorName(lz4_ret));
            ret = qe_err_common;
            goto __exit;
        }
    }

__exit:
    if (writer)
        LZ4F_writeClose(writer);
    if (inpfp)
        fclose(inpfp);
    if (outfp)
        fclose(outfp);
    if (buf)
        qe_free(buf);
    return ret;
}

static void usage(void)
{
    printf("\n");
    printf("lz4-test <cmd> <opt>\n");
    printf("  -h,?,--help          print help information\n");
    printf("  -i,--input           input file\n");
    printf("  -o,--output          output file\n");
    printf("  -d,--decompare       decompare mode\n");
    printf("\n");
}

int main(int argc, char *argv[])
{
    int opt;
    int decompare = 0;
    char *input_file  = QE_NULL;
    char *output_file = QE_NULL;

    static const struct option long_opts[] = {
        {"help",      no_argument,       NULL,  'h'},
        {"input",	  required_argument, NULL,  'i'},
        {"output",    required_argument, NULL,  'o'},
        {"decompare", no_argument,       NULL,  'd'},
        {NULL, 0, NULL, 0}
    };

    qelog_init(QELOG_DEBUG, QELOG_HMS|QELOG_LV|QELOG_CL);

    while ((opt = getopt_long(argc, argv, "i:o:d?h-", long_opts, NULL)) != -1) {
        
        switch (opt) {
        
        case 'i':
            input_file = optarg;
            break;

        case 'o':
            output_file = optarg;
            break;

        case 'd':
            decompare = 1;
            break;

        case '?':
        case 'h':
        default:
            usage();
            exit(EXIT_SUCCESS);
        }
    }

    if (!input_file || !output_file) {
        qe_error("invalid param");
        return 0;
    }

    if (decompare) {
        qe_info("decpmpare %s->%s", input_file, output_file);
        lz4_file_decompare(input_file, output_file);
    } else {
        qe_info("cpmpare %s->%s", input_file, output_file);
        lz4_file_compare(input_file, output_file);
    }
}